package cn.com.utils;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 日期时间实用程序
 *
 * @author LiuGuodong
 * @date 2024/05/29
 */
public class DateTimeUtils {


    public static final String BILLING_TIME_ZONE = System.getenv("BILLING_TIME_ZONE");

    /**
     * 获取账单时间，考虑时区
     *
     * @param billTime 原始账单时间
     *
     * @return {@link DateTime} 调整后的账单时间
     */
    public static DateTime getBillTime(DateTime billTime) {
        if (billTime == null) {
            return billTime;
        }
        //将当前时间时区转换为指定时区
        ZonedDateTime zonedDateTime = toZonedDateTime(billTime).withZoneSameInstant(getZone());
        return toDateTime(zonedDateTime);
    }


    /**
     * 获取系统默认时区或环境变量中的时区
     *
     * @return 时区字符串
     */
    private static ZoneId getZone() {
        return StrUtil.isBlank(BILLING_TIME_ZONE) ? ZoneId.of("UTC") : ZoneId.of(BILLING_TIME_ZONE);
    }

    /**
     * 格式化日期时间为指定格式
     *
     * @param dateTime 日期时间
     * @param format 格式字符串
     *
     * @return 格式化后的日期时间字符串
     */
    public static String formatDateTime(DateTime dateTime, String format) {
        return dateTime == null ? null : DateUtil.format(dateTime, format);
    }

    /**
     * 解析日期时间字符串为DateTime对象
     *
     * @param dateTimeStr 日期时间字符串
     * @param format 格式字符串
     *
     * @return {@link DateTime} 解析后的日期时间对象
     */
    public static DateTime parseDateTime(String dateTimeStr, String format) {
        return StrUtil.isBlank(dateTimeStr) ? null : DateUtil.parse(dateTimeStr, format);
    }

    /**
     * 计算两个日期时间之间的差异（天）
     *
     * @param startDate 起始日期时间
     * @param endDate 结束日期时间
     *
     * @return 两个日期时间之间的差异天数
     */
    public static long calculateDifferenceInDays(DateTime startDate, DateTime endDate) {
        if (startDate == null || endDate == null) {
            throw new IllegalArgumentException("Start date and end date must not be null");
        }
        return DateUtil.betweenDay(startDate, endDate, true);
    }

    /**
     * 将DateTime转换为ZonedDateTime
     *
     * @param dateTime 日期时间
     *
     * @return {@link ZonedDateTime} 转换后的ZonedDateTime对象
     */
    public static ZonedDateTime toZonedDateTime(DateTime dateTime) {
        return toZonedDateTime(dateTime, null);
    }

    /**
     * 日期时间
     *
     * @param zonedDateTime 日期时间
     *
     * @return {@link ZonedDateTime }
     */
    public static DateTime toDateTime(ZonedDateTime zonedDateTime) {
        return new DateTime(zonedDateTime.toLocalDateTime());
    }

    /**
     * 将DateTime转换为ZonedDateTime
     *
     * @param dateTime 日期时间
     *
     * @return {@link ZonedDateTime} 转换后的ZonedDateTime对象
     */
    public static ZonedDateTime toZonedDateTime(DateTime dateTime, ZoneId zone) {
        if (dateTime == null) {
            return null;
        }
        if (zone == null) {
            zone = ZoneId.systemDefault();
        }
        return dateTime.toLocalDateTime().atZone(zone);
    }

    /**
     * 获取当前时间的DateTime对象
     *
     * @return 当前时间的DateTime对象
     */
    public static DateTime getCurrentDateTime() {
        return DateTime.now();
    }

    /**
     * 将配置时间转换为系统默认时区
     *
     * @param dateTime 日期时间
     *
     * @return {@link DateTime} 转换为系统默认时区的日期时间
     */
    public static DateTime convertToSystemDefaultZone(DateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        // 将指定时区的时间转换为系统默认时区
        ZonedDateTime zonedDateTime = toZonedDateTime(dateTime, getZone())
                .withZoneSameInstant(ZoneId.systemDefault());
        return toDateTime(zonedDateTime);
    }

    /**
     * 将配置时间转换为系统默认时区
     *
     * @param dateTime 日期时间
     *
     * @return {@link DateTime} 转换为系统默认时区的日期时间
     */
    public static DateTime convertToSystemDefaultZone(DateTime dateTime, ZoneId zoneId) {
        if (dateTime == null) {
            return null;
        }
        // 将指定时区的时间转换为系统默认时区
        ZonedDateTime zonedDateTime = dateTime.toLocalDateTime().atZone(zoneId)
                                              .withZoneSameInstant(ZoneId.systemDefault());
        return new DateTime(zonedDateTime);
    }

    public static void main(String[] args) {
        // 示例账单时间 (假设为当前时间)
        DateTime billTime = new DateTime();

        // 打印原始账单时间
        System.out.println("原始账单时间: " + billTime);

        // 获取调整后的账单时间
        DateTime adjustedBillTime = getBillTime(billTime);
        System.out.println("调整后的账单时间: " + adjustedBillTime);

        //
        DateTime dateTime = convertToSystemDefaultZone(adjustedBillTime);
        System.out.println("系统默认时区: " + dateTime);

        // 打印不同时区的调整后账单时间
        printAdjustedTimes(billTime);

    }


    /**
     * 打印调整时间
     *
     * @param billTime 帐单时间
     */
    private static void printAdjustedTimes(DateTime billTime) {
        ZoneId systemDefaultZone = ZoneId.systemDefault();
        System.out.println("系统默认时区: " + systemDefaultZone);
        // 获取所有可用的时区标识符
        Set<String> allAvailableTimeZones = getAllAvailableTimeZones();
        List<ZoneId> zoneIds = allAvailableTimeZones.stream().map(ZoneId::of).collect(Collectors.toList());

        ZonedDateTime systemZonedDateTime = billTime.toLocalDateTime().atZone(systemDefaultZone);

        for (ZoneId zoneId : zoneIds) {
            ZonedDateTime zonedDateTime = systemZonedDateTime.withZoneSameInstant(zoneId);
            DateTime adjustedBillTimeByZone = new DateTime(zonedDateTime);
            System.out.println(zoneId + " 调整后的时间: " + adjustedBillTimeByZone);
        }
    }

    /**
     * 获取所有可用时区
     */
    private static Set<String> getAllAvailableTimeZones() {
        return ZoneId.getAvailableZoneIds();
    }
}
